package com.zn;

import java.io.*;

/**
 * @Description:
 * @Author: zhangnan
 * @Date: 2019/11/30
 * --类加载器的定义
 *    主要是定义类加载类的方式 ，如从其他磁盘，网络，数据库等地方加载class文件的二进制文件。
 *    父类的defineClass方法会将二进制转化为class对象
 *    对二进制文件的处理是jvm内部的一个本地方法实现。
 *
 *    主要流程是重新class方法
 *
 * --双亲委派
 *     作用： 避免污染核心类  ，爸爸能加载就不让儿子加载，爸爸要优先。
 *           越是顶层的classloader加载的class文件越是核心
 *           可以创建不同的非父子关系的类加载器，形成相互隔离的java类空间，这在很多框架中有应用
 *
 *     jvm在加载class文件时，当前的classloader不会自行加载该文件
 *     而是会委派其父类的classloader尝试加载，委派过程会一直到达顶级classloader---bootstrap classloader
 *     随后如果顶级父类不能加载（每个classloader都有自己对应要加载的class路劲，
 *          bootstrap classloader 会加载运行时环境所需要的jar包，核心类库，内嵌于jvm的一段机器码，c语言编写  rt.jar 同时启动系统类加载器和扩展类加载器
 *          sun.boot.class.path
 *          其子类 Ext classloader 扩展类加载器 会加载一些扩展包,其路劲也可由参数指定  jre\lib\ext
 *          java.ext.dirs
 *          Ext 的子类 AppClassLoader 会加载当前工程的classpath下的class文件
 *          java.class.path
 *
 *          系统类加载器可以显示的修改只需要添加一个运行属性
 *          -Djava.system.class.loader=com.zn.myClassLoader 即可将系统的类加载器修改
 *          一些框架组件利用此方法破快双亲委托实现自己的类加载器
 *
 *     ），如需加载其他类路径下的class文件则需要自定义classloader
 *
 * --classloader
 *      classloader 其实就是一个在确定了路径的前提下读取文件的一个类
 *      他将类路径下的文件分为两种：
 *      1、class文件  用于加载class文件
 *      2、资源文件   类路径下的文件可以用AppClassLoader使用文件名直接加载，不需要知道文件的具体地址
 *
 * --命名空间
 *     每个classloader都有一个命名空间
 *     命名空间由该类加载器和所有父加载器加载的类组成
 *     意思就是
 *     子加载器加载的类能看到他和他所有父类加载的类
 *     父类加载的类却看不到子加载器加载的类，被隔离开
 *     不同命名空间的class之间不可见，不能调用
 *
 *     在同一个命名空间内不会出现全类名完全相同的两个类
 *     不同命名空间可能会出现
 */
public class MyClassLoader extends ClassLoader {

    private String classLoaderName;

    private String otherClassPath;

    private static final  String EXTENSION = ".class";

    MyClassLoader(){}

    public MyClassLoader(String classLoaderName,String otherClassPath){
        super();
        this.classLoaderName = classLoaderName;
        this.otherClassPath = otherClassPath;
    }

    public MyClassLoader(ClassLoader classLoader,String classLoaderName,String otherClassPath){
        super(classLoader);
        this.classLoaderName = classLoaderName;
        this.otherClassPath = otherClassPath;
    }

    public String getClassLoaderName() {
        return classLoaderName;
    }

    public String getOtherClassPath() {
        return otherClassPath;
    }

    @Override
    public Class<?> findClass(String className) {
        byte[] bytes = this.loadClassData(className);
        assert bytes != null;
        return this.defineClass(className,bytes,0,bytes.length);
    }

    private byte[] loadClassData(String className)  {

        String filePath = otherClassPath + className.replace(".","\\") + EXTENSION;

        try(
            FileInputStream fis = new FileInputStream(new File(filePath));
            ByteArrayOutputStream bos = new ByteArrayOutputStream()
        ){
            byte[] buf = new byte[1024];
            int len;
            while((len = fis.read(buf)) != -1){
                bos.write(buf,0,len);
            }
            return bos.toByteArray();
        }catch (Exception ex){
            ex.printStackTrace();
        }

        return null;
    }

}
